Skip to content

Blocks: Introduce WP_Block_Node class.#10796

Draft
dmsnell wants to merge 1 commit intoWordPress:trunkfrom
dmsnell:blocks/add-block-node
Draft

Blocks: Introduce WP_Block_Node class.#10796
dmsnell wants to merge 1 commit intoWordPress:trunkfrom
dmsnell:blocks/add-block-node

Conversation

@dmsnell
Copy link
Member

@dmsnell dmsnell commented Jan 26, 2026

Experimenting on adding a stable record-type class with helpers for holding raw block properties as parsed from a serialized document.

@dlh01
Copy link

dlh01 commented Jan 29, 2026

@dmsnell Thanks for putting this together after the discussion in #10735. If this experiment is ready for feedback (whenever it's ready, really), I have many use cases that I would be excited to test it against from a DX perspective.

@dmsnell
Copy link
Member Author

dmsnell commented Jan 29, 2026

@dlh01 by all means! please do share. If you are willing I think it would be nice to get more feedback on how this can be or would be used.

My primary goal would be to keep this class mostly a “record,” being more or less an associative array whose keys are known up-front and documented. I do think that these little helper methods are useful to address really common concerns though, like answering “is this a (core/)gallery?” and “does this have the block supports attribute?” I wish I had focused on these development needs earlier before we standardized the array form, which was meant to keep things simple.

The more code we can examine and play with, the better!

@dlh01
Copy link

dlh01 commented Feb 22, 2026

Hey @dmsnell — after completing a task for a client recently, I went back to see how I might have completed the same task with WP_Block_Node, and I have some initial feedback to share based on that experience.

The task was to create a core/buttons block with inner core/button blocks. Here's what I ended up with using WP_Block_Node, with a few comments below:

$node = WP_Block_Node::make(
	'core/buttons',
	[
		'style' => [
			'spacing' => [
				'blockGap' => [
					'top' => 'var:preset|spacing|foo-n-1',
				],
			],
		],
	],
	...[ // extra level of array nesting is needed to be able to spread the `array_map()` below
		'<div class="wp-block-buttons">',
		...array_map(
			function ( int $post_id ) {
				$post = get_post( $post_id );

				if ( ! $post instanceof WP_Post ) {
					return WP_Block_Node::make_freeform( '' );
				}

				// logic to determine $button_text...

				return WP_Block_Node::make_freeform( // not actually correct, this template contains block markup, not freeform HTML
					Timber::compile(
						__DIR__ . '/parts/button.html.twig',
						[
							'url'  => get_permalink( $post ),
							'text' => $button_text,
						],
					),
				);
			},
			$translations->post_ids(),
		),
		'</div>',
	],
);
  1. This was a case where having the $inner_content parameter in ::make() be variadic made it harder to work with. I couldn't put my array_map() between the opening and closing DIV tags because the spread operator has to be the last argument in PHP. As you can see, the workaround was to add an extra level of array nesting and then spread that array into $inner_content. In this case, it would have been simpler if $inner_content accepted a plain array.

  2. It would be handy to have a static constructor that accepted a string and created the node out of the first block that was parsed from it. In my case, my button.html.twig template contains a single button block, so I would have liked to be able to pass that output. (If I make a mistake and pass a string that contains multiple top-level blocks, then that's my fault, and I get only the first one back, or I get an error.) Ideally, this constructor would also parse a string of freeform HTML into a freeform block, so that I could use it for both cases without having to think about it.

  3. I'm guessing this was unintentional and would have been noticed sooner or later, but the inner blocks returned from ::to_block_array() are still instances of WP_Block_Node, not arrays, so the array can't be passed to serialize_blocks() as-is.

I'll keeping looking out for these opportunities and will share more feedback as I can. Thanks!

@dmsnell
Copy link
Member Author

dmsnell commented Feb 24, 2026

this is awesome feedback @dlh01 — I will get back to it, but not today

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants